﻿using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Web;
using System.Data.SqlClient;
using System.Configuration;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.Web.SessionState;
using HttpLogger.Extensions;

namespace HttpLogger
{
    public class SimpleLogger : IHttpModule
    {
        private IAuditLogger _auditLogger;
        private string _filter;
        private string _formFilter;
        private string _fileFilter;

        // Because no one wants to do DI with an HttpModule and no money
        // This will crash the entire site if not found
        public SimpleLogger() : this(
            new SqlAuditLogger(
                new SqlConnectionFactory(ConfigurationManager.ConnectionStrings["HttpLogger"].ToString())), 
            ConfigurationManager.AppSettings["HttpLogger.Filters"].ToString(), 
            ConfigurationManager.AppSettings["HttpLogger.FormFilters"].ToString(), 
            ConfigurationManager.AppSettings["HttpLogger.FileFilters"].ToString()) { }

        public SimpleLogger(IAuditLogger auditLogger, string filter, string formFilter, string fileFilters)
        {
            _auditLogger = auditLogger;
            _filter = filter;
            _formFilter = formFilter;
            _fileFilter = fileFilters;
        }
        
        public void Dispose() { /* Nothing to dispose */ }
        
        public void Init(HttpApplication context)
        {
            // Only context handler found safe for requesting form variables in asp.net auto event wire up applications
            // In certain situations accessing the readonly form collection caused postback to fail so we access after to workaround
            context.PostRequestHandlerExecute += new EventHandler(Context_PostAcquireRequestState);
        }

        public void Context_PostAcquireRequestState(object sender, EventArgs e)
        {
            try
            {
                // Tests are fun, isolation is hard, do as little as possible
                HttpApplication app = (HttpApplication)sender;
                HttpSessionState session = null;
                try { session = app.Session; } catch (System.Web.HttpException) { /* Session throws a httpexception when unavailable */ }

                AuditVisit(app.Response.ContentType,
                    app.Request.Url,
                    String.Format("{0}:{1}", app.Request.ServerVariables["REMOTE_ADDR"], app.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]),
                    app.Request.Form,
                    app.Request.ServerVariables,
                    session,
                    app.Request.Headers,
                    app.Modules,
                    app.Request.Cookies
                    );
            }
            catch (Exception) 
            {
                // Got nothing to do but don't crash the site, we could log an event
                // if (EventLog.SourceExists("HttpLogger")) EventLog.WriteEntry("HttpLogger", exp.Message, EventLogEntryType.Error);
            }
        }

        public void AuditVisit(string contentType,
            Uri url,
            string ip,
            ICollection form,
            ICollection serverVariables,
            ICollection session,
            ICollection headers,
            ICollection modules,
            ICollection cookies)
        {
            var curFile = Path.GetFileName( url.AbsolutePath );

            if (Regex.Match(contentType, _filter).Success
                && Regex.Match(curFile, _fileFilter).Success)
            {
                var srv = (NameValueCollection)serverVariables;
                
                var curAudit = new AuditLog() { 
                    ResponseType = contentType, 
                    Url = url.LocalPath.ToString(),
                    IP = ip,
                    RequestData = url.PathAndQuery,
                    SessionData = session.SpecializedToString(),
                    ModulesData = modules.SpecializedToString(),
                    FormData = form.SpecializedToString(_formFilter),
                    HeaderData = headers.SpecializedToString(),
                    CookieData = cookies.SpecializedToString(),
                    ServerData = serverVariables.SpecializedToString()
                };
                
                _auditLogger.LogRecord(curAudit);
            }
        }

    }
}